'use strict'; const { CompoundCommandBuilder, DocumentCommand, AddChildNodesCommandBuilder, NodeChildType, NodeMoveType } = require('/commands.js'); const { Dialog, DialogResult, UnitType } = require('/dialog.js'); const { Document } = require('/document.js'); const { Transform } = require('/geometry.js'); const { Selection } = require('/selections.js'); const { ContainerNodeDefinition } = require('/nodes.js'); function isOk(result) { return (result?.value ?? result) === DialogResult.Ok.value; } function buildBrickPattern(doc, spread, sourceNode, cols, rows, gapX, gapY, offsetPercent, previewGroupRef) { // Delete the previous preview group — which now correctly contains all bricks if (previewGroupRef.group) { try { previewGroupRef.group.delete(); } catch(e) {} previewGroupRef.group = null; } doc.executeCommand(DocumentCommand.createSetCurrentSpread(spread)); const bb = sourceNode.getSpreadBaseBox(false); const stepX = bb.width + gapX; const stepY = bb.height + gapY; const brickOffsetX = stepX * (offsetPercent / 100); // Step 1: Create a container group to hold all bricks const containerBuilder = AddChildNodesCommandBuilder.create(); containerBuilder.setInsertionTarget(spread); containerBuilder.addContainerNode(ContainerNodeDefinition.create('Brick Pattern')); const containerCmd = containerBuilder.createCommand(false, NodeChildType.Main); doc.executeCommand(containerCmd); const containerArr = []; containerCmd.newNodes.forEach(n => containerArr.push(n)); if (containerArr.length === 0) { console.log('ERROR: No container created'); return; } const container = containerArr[0]; doc.executeCommand(DocumentCommand.createSetCurrentSpread(spread)); // Step 2: Duplicate the source node once per brick, capturing each new node const srcSel = Selection.create(doc, sourceNode); const allNewNodes = []; for (let row = 0; row < rows; row++) { const rowOffsetX = (row % 2 === 1) ? brickOffsetX : 0; for (let col = 0; col < cols; col++) { const tx = col * stepX + rowOffsetX; const ty = row * stepY; const xf = Transform.createTranslate(tx, ty); const dupCmd = DocumentCommand.createTransform(srcSel, xf, { duplicateNodes: true }); doc.executeCommand(dupCmd); dupCmd.newNodes.forEach(n => allNewNodes.push(n)); } } // Step 3: Move all duplicated bricks into the container group doc.executeCommand(DocumentCommand.createSetCurrentSpread(spread)); const moveCompound = CompoundCommandBuilder.create(); for (const node of allNewNodes) { const nodeSel = Selection.create(doc, node); moveCompound.addCommand(DocumentCommand.createMoveNodes(nodeSel, container, NodeMoveType.Inside, NodeChildType.Main)); } doc.executeCommand(moveCompound.createCommand()); previewGroupRef.group = container; } // ---- MAIN ---- const doc = Document.current; if (!doc) { console.log('ERROR: No document open.'); } else { const nodes = doc.selection.nodes; if (nodes.length === 0) { console.log('Please select an object to use as the brick tile.'); } else { const sourceNode = nodes.at(0); const spread = doc.currentSpread; const previewGroupRef = { group: null }; const dlg = Dialog.create('Brick Pattern Generator'); const col = dlg.addColumn(); const g1 = col.addGroup('Columns'); const colsCtrl = g1.addUnitValueEditor('Columns', UnitType.Number, UnitType.Number, 6, 1, 200).setPrecision(0); // const g2 = col.addGroup('Rows'); const rowsCtrl = g1.addUnitValueEditor('Rows', UnitType.Number, UnitType.Number, 6, 1, 200).setPrecision(0); // const g3 = col.addGroup('Gap X (px)'); const gapXCtrl = g1.addUnitValueEditor('Gap X (px)', UnitType.Number, UnitType.Number, 4, -1000, 1000).setPrecision(1); // const g4 = col.addGroup('Gap Y (px)'); const gapYCtrl = g1.addUnitValueEditor('Gap Y (px)', UnitType.Number, UnitType.Number, 4, -1000, 1000).setPrecision(1); // const g5 = col.addGroup('Row offset (% of brick width)'); const offsetCtrl = g1.addUnitValueEditor('Row offset (% of brick width)', UnitType.Number, UnitType.Number, 50, 0, 100).setPrecision(0); // const g6 = col.addGroup('Preview'); const previewBtn = g1.addButton('Preview'); previewBtn.onClickHandler = () => { buildBrickPattern(doc, spread, sourceNode, Math.max(1, Math.round(colsCtrl.value)), Math.max(1, Math.round(rowsCtrl.value)), gapXCtrl.value, gapYCtrl.value, offsetCtrl.value, previewGroupRef); }; dlg.initialWidth = 420; const result = dlg.runModal(); if (isOk(result)) { buildBrickPattern(doc, spread, sourceNode, Math.max(1, Math.round(colsCtrl.value)), Math.max(1, Math.round(rowsCtrl.value)), gapXCtrl.value, gapYCtrl.value, offsetCtrl.value, previewGroupRef); } else { if (previewGroupRef.group) { try { previewGroupRef.group.delete(); } catch(e) {} previewGroupRef.group = null; } } } }